using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Runtime.InteropServices;

namespace BAFIRCSharp
{
    enum IRCConnectionState
    {
        Connected,
        Disconnected
    }

    public class IRCConnection
    {
        // Delegates
        public delegate void StateChangeD(IRCConnection conn, IRCConnectionState state);

        // Connection handeling
        private Socket IRC = null;
        private ServerInfo ConnInfo;
        private bool _connected = false; public bool Connected { get { return _connected; } }
        
        // Buffering / queue
        private string RecieveBuffer = "";
        private DateTime LastSend = DateTime.Now;
        private string SendBuffer = "";
        public bool SendQueueData { get { return SendBuffer.Length != 0; } }

        // Configuration
        public event StateChangeD OnStateChange;

        public IRCConnection()
        {
        }

        public bool Connect(ServerInfo Server)
        {
            if (_connected)
                return false;

            ConnInfo = Server;

            try
            {
                IRC = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
                IPHostEntry ServerIP = Dns.GetHostEntry(Server.Server);
                IPAddress ip = ServerIP.AddressList[0];
                //IPAddress ip = IPAddress.Parse(Dns.Resolve(Server.Server));
                IPEndPoint EndPoint = new IPEndPoint(ip, Server.Port);
                IRC.Connect(EndPoint);

                while (!IRC.Connected)
                    System.Threading.Thread.Sleep(100);
            }
            catch (SocketException se)
            {
                Console.WriteLine(se.Message);
                return false;
            }

            _connected = true;
            //OnStateChange(this, IRCConnectionState.Connected);
            return true;
        }

        public bool ReConnect()
        {
            return Connect(ConnInfo);
        }

        public void Disconnect()
        {
            if (_connected)
            {
                _connected = false;
                IRC.Disconnect(false);
                //OnStateChange(this, IRCConnectionState.Disconnected);
            }
        }

        public bool Poll() // Poll, getting waiting data and working on the send queue. Returns true if there is a message waiting
        {
            if (!_connected)
                return false;

            if (IRC.Poll(1, SelectMode.SelectRead))
            {
                if (IRC.Available > 0)
                {
                    byte[] buff = new byte[IRC.Available];
                    IRC.Receive(buff);
                    RecieveBuffer += Encoding.Default.GetString(buff);
                    //Console.Write(Encoding.Default.GetString(buff));
                }
                else
                {
                    SendBuffer = "";
                    RecieveBuffer = "";
                    IRC.Disconnect(true);
                    _connected = false;
                }
            }

            if (!IRC.Connected)
            {
                _connected = false;
                //OnStateChange(this, IRCConnectionState.Disconnected);
                return false;
            }

            // Send one message from Send Buffer
            PollSendQueue();

            return ((RecieveBuffer.IndexOf("\r\n") < 0) ? RecieveBuffer.IndexOf("\n\r") : RecieveBuffer.IndexOf("\r\n")) > 0;
        }

        public IRCMessage GetMessage()
        {
            if (RecieveBuffer.IndexOf("\r\n") < 0 && RecieveBuffer.IndexOf("\n\r") < 0)
                return null; // No messages waiting, fool

            int MsgEnd = (RecieveBuffer.IndexOf("\r\n") < 0) ? RecieveBuffer.IndexOf("\n\r") : RecieveBuffer.IndexOf("\r\n");

            string Message = RecieveBuffer.Substring(0, MsgEnd + 2);
            RecieveBuffer = RecieveBuffer.Substring(MsgEnd + 2);
            //RecieveBuffer = RecieveBuffer.Remove(0, MsgEnd);

            return new IRCMessage(Message);
        }

        public void FlushQ()
        {
            SendBuffer = "";
        }

        public void PollSendQueue()
        {
            if (SendBuffer.IndexOf("\r\n") < 0 && SendBuffer.IndexOf("\n\r") < 0)
                return; // No messages waiting, fool

            DateTime NextSendMin = LastSend.AddMilliseconds(2000);
            if (NextSendMin > DateTime.Now)
                return;

            int MsgEnd = (SendBuffer.IndexOf("\r\n") < 0) ? SendBuffer.IndexOf("\n\r") : SendBuffer.IndexOf("\r\n");

            string Message = SendBuffer.Substring(0, MsgEnd + 2);
            if (Message.Length > 512)
            {
                Console.WriteLine("Message too long: " + Message);
                Message = Message.Substring(0, 510) + "\r\n";
            }
            //Console.Write(Message);
            SendBuffer = SendBuffer.Substring(MsgEnd + 2);

            try
            {
                byte[] Data = Encoding.ASCII.GetBytes(Message);
                IRC.Send(Data);
            }
            catch (SocketException se)
            {
                //MessageBox.Show(se.Message);
                Console.WriteLine("HELP! " + se.Message);
            }

            LastSend = DateTime.Now;
        }

        public void SendMessage(string Message)
        {
            SendBuffer += Message;
        }

        public void SendMessage(Messages.MessageBase Message)
        {
            SendBuffer += Message.BuildMessage();
        }
    }
}
